import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import * as dayjs from 'dayjs';
import { nanoid } from 'nanoid';
import AccessTokenEntity from 'src/entities/access-token.entity';
import LetterViewEntity from 'src/entities/letter-view.entity';
import LetterEntity from 'src/entities/letter.entity';
import { uploadFile } from 'src/utils/file';
import { EntityManager, Not, Repository } from 'typeorm';
import { DeleteLetterDTO, GetLetterDetailDTO, GetUserLetterListDTO, InsertLetterDTO, UpdateLetterDTO } from './letter.dto';

@Injectable()
export class LetterService {
  constructor(
    @InjectRepository(LetterEntity)
    private readonly letterEntityRepository: Repository<LetterEntity>,
    @InjectRepository(AccessTokenEntity)
    private readonly accessTokenEntityRepository: Repository<AccessTokenEntity>,
    @InjectRepository(LetterViewEntity)
    private readonly letterViewEntityRepository: Repository<LetterViewEntity>,
    @InjectEntityManager() private entityManager: EntityManager,
    private readonly httpService: HttpService,
    private readonly configService: ConfigService
  ){}

  async insertLetterImage(ImageData:string){
    let resData={
      code:0,
      data:{},
      msg:''
    }
    await uploadFile(
      ImageData,
      'base64',
      'letterImage/'
    )
      .then((res:{
        code:number,
        data:any,
        msg:string
      })=>{
        if(res.code===200){
          resData={
            code:200,
            data:{
              ImageUrl: `http://${res.data.ImageUrl}`,
            },
            msg:'保存图片成功!'
          }
        }
        else{
          resData={
            code:500,
            data:{
              res
            },
            msg:'保存图片失败!'
          }
        }
        return resData
      })
      .catch(e=>{
        resData={
          code:500,
          data:{
            e
          },
          msg:'保存图片失败!'
        }
        return resData
      })
    return resData
  }

  async insertLetter(insertLetter:InsertLetterDTO){
    let resData={
      code:0,
      data:{},
      msg:''
    }
    const {ToName='',FromName='',Content='',Author}=insertLetter
    //判断情书是否已经超过5封，超过后不允许再新增
    const letterCount =await this.letterEntityRepository.count({
      where:{
        Author:insertLetter.Author
      }
    })
    if(letterCount>5){
      resData={
        code:400,
        data:{},
        msg:'目前只支持每个人5封情书!'
      }
    }
    const now=dayjs().format('YYYY-MM-DD HH:mm:ss')

    await this.entityManager.transaction(async entityManager=>{
      //新增情书
      const newSysID=nanoid(20)
      const newLetter=new LetterEntity();
      newLetter.SysID=newSysID;
      newLetter.ToName=ToName;
      newLetter.FromName=FromName;
      newLetter.CodeUrl='';
      newLetter.Content=Content;
      newLetter.Author=Author;
      newLetter.CreateTime=now;
      newLetter.UpdateTime=now;
      newLetter.Deleted=0;
      await entityManager.save(LetterEntity,newLetter)

      //#region 以下为生成特定页面小程序二维码
      const accessTokenList=await entityManager.findOne(AccessTokenEntity,{
        select:['SysID','AccessToken','ExpiresTime'],
        where:{
          SysID:Not(-1)
        },
        order:{
          CreateTime:'DESC'
        }
      })
      const {SysID=-1,ExpiresTime=''}=accessTokenList;
      let {AccessToken=''}=accessTokenList;

      if(SysID===-1){
        const resAccessToken=await this.computeAccessToken();
        if(resAccessToken.code===200){
          const { access_token = '', expires_in = 7200 }=resAccessToken.data as any;
          const newExpiresTime= dayjs().add(expires_in - 120, 's').format('YYYY-MM-DD HH:mm:ss');
          await entityManager.query(`
            DELETE FROM AccessToken;
          `)
          const accessTokenEntity=new AccessTokenEntity();
          accessTokenEntity.AccessToken=access_token;
          accessTokenEntity.ExpiresTime=newExpiresTime;
          accessTokenEntity.CreateTime=now
          await entityManager.save(AccessTokenEntity,accessTokenEntity)
          AccessToken=access_token;
        }
      }
      else{
        if(dayjs().diff(dayjs(ExpiresTime),'s')>0){
          //AccessToken已过期重新获取
          const resAccessToken=await this.computeAccessToken();
          if(resAccessToken.code===200){
            const { access_token = '', expires_in = 7200 }=resAccessToken.data as any;
            const newExpiresTime= dayjs().add(expires_in - 120, 's').format('YYYY-MM-DD HH:mm:ss');
            await entityManager.query(`
              DELETE FROM AccessToken;
            `)
            const accessTokenEntity=new AccessTokenEntity();
            accessTokenEntity.AccessToken=access_token;
            accessTokenEntity.ExpiresTime=newExpiresTime;
            accessTokenEntity.CreateTime=now
            await entityManager.save(AccessTokenEntity,accessTokenEntity)
            AccessToken=access_token;
          }
        }
      }

      if(AccessToken!==''){
        const wxUrl=this.configService.get('WECHAT_CONFIG').url
        const resCode = await this.httpService.axiosRef.get(`
          ${wxUrl}/wxa/getwxacodeunlimit?access_token=${AccessToken}
        `)
        if(resCode.status===200){
          const resCodeUrl= await uploadFile(resCode.data,'buffer','letterCode/') as {code:number,data:any,msg:string};
          if(resCodeUrl.code===200){
            const updateLetter=new LetterEntity();
            updateLetter.CodeUrl=resCodeUrl.data.ImageUrl;
            resData.data={
              LetterCodeUrl:resCodeUrl.data.ImageUrl
            }
            await entityManager.update(LetterEntity,{
              SysID:newSysID
            },updateLetter)
          }

        }
      }
      //#endregion
    })
      .then(()=>{
        resData.code=200;
        resData.msg='新增情书成功!'
      })
      .catch(e=>{
        resData={
          code:200,
          data:{
            e
          },
          msg:'新增情书失败!'
        }
      })
    return resData
  }

  async updateLetter(updateLetter:UpdateLetterDTO){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    try{
      const {SysID='', ToName='',FromName='',Content=''}=updateLetter
      const updateLetterEntity=new LetterEntity()
      updateLetterEntity.ToName=ToName;
      updateLetterEntity.FromName=FromName;
      updateLetterEntity.Content=Content;
      updateLetterEntity.UpdateTime=dayjs().format('YYYY-MM-DD HH:mm:ss')
      await this.letterEntityRepository.update({
        SysID:SysID
      },updateLetter)
      resData={
        code:200,
        data:{},
        msg:'更新情书成功!'
      }
    }
    catch(e){
      resData={
        code:500,
        data:{
          e
        },
        msg:''
      }
    }
    return resData
  }

  async deleteLetter(deleteLetter:DeleteLetterDTO){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    try{
      const {SysID=''}=deleteLetter
      const deleteLetterEntity=new LetterEntity()
      deleteLetterEntity.Deleted=1
      deleteLetterEntity.UpdateTime=dayjs().format('YYYY-MM-DD HH:mm:ss')
      await this.letterEntityRepository.update({
        SysID:SysID
      },deleteLetterEntity)
      resData={
        code:200,
        data:{},
        msg:'删除情书成功!'
      }
    }
    catch(e){
      resData={
        code:500,
        data:{
          e
        },
        msg:'删除情书失败'
      }
    }
    return resData
  }

  async computeAccessToken(){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    const appid=this.configService.get('WECHAT_CONFIG').appid
    const secret=this.configService.get('WECHAT_CONFIG').secret
    const wxUrl=this.configService.get('WECHAT_CONFIG').url
    const resAccessToken = await this.httpService.axiosRef.get(`
      ${wxUrl}/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}
    `)
    if(resAccessToken.status === 200){
      const {access_token='',expires_in=7200}=resAccessToken.data;
      resData={
        code:200,
        data:{
          access_token:access_token,
          expires_in:expires_in
        },
        msg:'获取AccessToken成功!'
      }
    }
    else{
      resData={
        code:resAccessToken.status,
        data:{
          error:resAccessToken.data
        },
        msg:'获取AccessToken失败!'
      }
    }
    return resData
  }

  async getUserLetterList(getUserLetterList:GetUserLetterListDTO){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    try{
      const {Type='',UserSysID=-1}=getUserLetterList;
      if(Type==='send'){
        resData = await this.getUserSendLetterList(UserSysID);
      }
      else{
        resData = await this.getUserReceiveLetterList(UserSysID)
      }
    }
    catch(e){
      resData={
        code:500,
        data:{
          e
        },
        msg:''
      }
    }
    return resData
  }

  async getUserSendLetterList(UserSysID:number){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    try{
      const letterList= await this.letterEntityRepository.findAndCount({
        select:[
          'SysID','FromName','ToName','CodeUrl'
        ],
        where:{
          Author:UserSysID,
          Deleted:0
        }
      })
      resData.data={
        list:letterList[0],
        total:letterList[1]
      }
      resData.msg='获取用户寄出情书列表成功!'
    }
    catch(e){
      resData={
        code:500,
        data:{
          e
        },
        msg:'获取用户寄出情书列表失败!'
      }
    }
    return resData
  }

  async getUserReceiveLetterList(UserSysID:number){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    try{
      const letterList = await this.letterViewEntityRepository
        .createQueryBuilder('LV')
        .select([
          'LV.LetterSysID SysID','L.FromName FromName','L.ToName ToName','L.CodeUrl CodeUrl'
        ])
        .innerJoin('Letter','L','LV.LetterSysID=L.SysID AND L.Deleted=0')
        .where('LV.Deleted=0 AND LV.UserSysID=:UserSysID',{UserSysID:UserSysID})
        .orderBy('LetterSysID','ASC')
        .getRawMany();
      const letterTotal=await this.letterViewEntityRepository
        .createQueryBuilder('LV')
        .select([
          'LV.LetterSysID SysID','L.FromName FromName','L.ToName ToName'
        ])
        .innerJoin('Letter','L','LV.LetterSysID=L.SysID AND L.Deleted=0')
        .where('LV.Deleted=0 AND LV.UserSysID=:UserSysID',{UserSysID:UserSysID})
        .orderBy('LetterSysID','ASC')
        .getCount()
      resData.data={
        list:letterList,
        total:letterTotal
      }
      resData.msg='获取用户收到情书列表成功!'
    }
    catch(e){
      resData={
        code:500,
        data:{
          e
        },
        msg:'获取用户收到情书列表失败!'
      }
    }
    return resData
  }

  async getLetterDetail(getLetterDetail:GetLetterDetailDTO){
    let resData={
      code:200,
      data:{},
      msg:''
    }
    try{
      const {SysID='',UserSysID=-1}=getLetterDetail;
      const count = await this.letterEntityRepository.count({
        where:{
          SysID:SysID,
          Deleted:0
        }
      })
      if(count>0){
        const letterDetail = await this.letterEntityRepository.findOne({
          select:['SysID','FromName','ToName','Content'],
          where:{
            SysID:SysID,
            Deleted:0
          }
        })
        //#region 关联LetterView表
        const letterViewCount=await this.letterViewEntityRepository.count({
          where:{
            LetterSysID:SysID,
            UserSysID:UserSysID,
            Deleted:0
          }
        })
        if(letterViewCount===0&&UserSysID!==-1){
          const now=dayjs().format('YYYY-MM-DD HH:mm:ss');
          const newLetterViewEntity=new LetterViewEntity()
          newLetterViewEntity.LetterSysID=SysID;
          newLetterViewEntity.UserSysID=UserSysID;
          newLetterViewEntity.CreateTime=now;
          newLetterViewEntity.UpdateTime=now;
          newLetterViewEntity.Deleted=0;
          await this.letterViewEntityRepository.save(newLetterViewEntity);
        }
        //#endregion
        resData.data=letterDetail;
        resData.msg='获取情书详情成功!'
      }
      else{
        resData.code=400;
        resData.msg='没有查找到该情书'
      }
    }
    catch(e){
      resData={
        code:500,
        data:{
          e
        },
        msg:'获取情书详情失败!'
      }
    }
    return resData
  }
}
